﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net;
using System.Runtime.Serialization.Json;
using System.IO;


namespace MyNet.WebUI {

    using DotNetOpenAuth.AspNet.Clients;
    using DotNetOpenAuth.Messaging;
    using Microsoft.AspNet.Membership.OpenAuth;

    internal static class AuthConfig {
        public static void RegisterOpenAuth () {
            // See http://go.microsoft.com/fwlink/?LinkId=252803 for details on setting up this ASP.NET
            // application to support logging in via external services.

            //OpenAuth.AuthenticationClients.AddTwitter(
            //    consumerKey: "your Twitter consumer key",
            //    consumerSecret: "your Twitter consumer secret");


            OpenAuth.AuthenticationClients.Add ( "Facebook", () => {
                return 
                    new FacebookOAuthClient ( 
                        applicationID: "460803017295472", 
                        applicationSecret: "34d34a5e378f990136ea8ab22bdeca32",
                        authScope: "email, read_friendlists" 
                    ); 
                }
            );

            //MessagingUtilities.AppendQueryArgument ( new UriBuilder (), "", "" );

            //OpenAuth.AuthenticationClients.AddMicrosoft(
            //    clientId: "your Microsoft account client id",
            //    clientSecret: "your Microsoft account client secret");

            //OpenAuth.AuthenticationClients.AddGoogle();
        }
    }


    public class FacebookOAuthClient : DotNetOpenAuth.AspNet.Clients.OAuth2Client {

        private const String AuthorizationEndpoint = "https://www.facebook.com/dialog/oauth";
        private const String TokenEndpoint = "https://graph.facebook.com/oauth/access_token";

        private readonly string appID;
        private readonly string appSecret;
        private readonly string authScope;

        public FacebookOAuthClient ( String applicationID, String applicationSecret, String authScope )
            : base ( "Facebook" ) {
                this.appID = applicationID;
                this.appSecret = applicationSecret;
                this.authScope = authScope;
        } // Constructor

        protected override Uri GetServiceLoginUrl( Uri returnUrl ) {
            
            var builder = new UriBuilder( AuthorizationEndpoint );

            MessagingUtilities.AppendQueryArgument( builder, "client_id", this.appID );
            MessagingUtilities.AppendQueryArgument( builder, "redirect_uri", returnUrl.AbsoluteUri );
            MessagingUtilities.AppendQueryArgument( builder, "scope", this.authScope );
 
            return builder.Uri;

        } // Method

        protected override IDictionary<string, string> GetUserData ( string accessToken ) {
            
            FacebookGraphData graphData;
            
            var request =
                WebRequest.Create (
                    //"https://graph.facebook.com/me?access_token=" + MessagingUtilities.EscapeUriDataStringRfc3986 ( accessToken ) );
                    "https://graph.facebook.com/me?access_token=" + accessToken );
            
            using ( var response = request.GetResponse () ) {
                using ( var responseStream = response.GetResponseStream () ) {

                    var serializer = new DataContractJsonSerializer ( typeof ( FacebookGraphData ) );
                    graphData = (FacebookGraphData) serializer.ReadObject ( responseStream );
                }
            }

            // Dictionary containing the public profil of the Facebook User
            var userData = new Dictionary<String, String> ();
            userData.Add( "id", graphData.Id );
            userData.Add( "username", graphData.Email );
            userData.Add( "name", graphData.Name );
            userData.Add( "link", graphData.Link == null ? null : graphData.Link.AbsoluteUri );
            userData.Add( "gender", graphData.Gender );
            userData.Add( "birthday", graphData.Birthday );

            return userData;

        } // Method

        protected override string QueryAccessToken ( Uri returnUrl, string authorizationCode ) {

            var builder = new UriBuilder ( TokenEndpoint );

            MessagingUtilities.AppendQueryArgument ( builder, "client_id", this.appID );
            MessagingUtilities.AppendQueryArgument ( builder, "redirect_uri", NormalizeHexEncoding(returnUrl.AbsoluteUri) );
            MessagingUtilities.AppendQueryArgument ( builder, "client_secret", this.appSecret );
            MessagingUtilities.AppendQueryArgument ( builder, "code", authorizationCode );
            MessagingUtilities.AppendQueryArgument ( builder, "scope", this.authScope );            

            using ( WebClient client = new WebClient () ) {
                string data = client.DownloadString ( builder.Uri );
                if ( string.IsNullOrEmpty ( data ) ) {
                    return null;
                }

                var parsedQueryString = HttpUtility.ParseQueryString ( data );
                return parsedQueryString["access_token"];
            }

        } // Method

        private static String NormalizeHexEncoding( string url ) {

            var chars = url.ToCharArray ();

            for ( int i = 0; i < chars.Length - 2; i++ ) {
                if ( chars[i] == '%' ) {
                    chars[i + 1] = char.ToUpperInvariant ( chars[i + 1] );
                    chars[i + 2] = char.ToUpperInvariant ( chars[i + 2] );
                    i += 2;
                }
            }

            return new String( chars );
        } // Method



        private static string EscapeUriDataStringRfc3986( string value ) {
            
            string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" };
            
            System.Text.StringBuilder escaped = new System.Text.StringBuilder ( Uri.EscapeDataString ( value ) );

            // Upgrade the escaping to RFC 3986, if necessary.
            for ( int i = 0; i < UriRfc3986CharsToEscape.Length; i++ ) {
                escaped.Replace ( UriRfc3986CharsToEscape[i], Uri.HexEscape ( UriRfc3986CharsToEscape[i][0] ) );
            }

            // Return the fully-RFC3986-escaped string.
            return escaped.ToString ();
        }

    } // Class

}